﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Unity.Errors.types;
using Unity.Errors.root;

namespace Unity.Test.Performance.timer
{
    public class TTurtle
    {

        public TTurtle()
        {
            InitClass();
        }

        private void InitClass()
        {
            _UnitCount = 5;
            _TimerOn = false;
            _UnitTimes = new TimeSpan[_UnitCount];
            _Index = 0;
            _Events = new TTimerEvents();
            _TimerRunningEventArgs = new TTimerRunningEventArgs();
        }

        #region private fields
        private TTimerRunningEventArgs _TimerRunningEventArgs;
        private DateTime _StartPoint;
        private DateTime _EvaluatedCompletionTime;
        private DateTime _LastPinnedTime;
        private TimeSpan[] _UnitTimes;
        private bool _TimerOn;
        private double _Denominator;
        private bool _DenominatorIsSet;
        private int _UnitCount;
        private int _Index;
        #endregion

        #region public methods
        public void Start(int denominator)
        {
            _Denominator = Convert.ToDouble(denominator);
            SErrorManager.Errors("BooleanFlagWrong")
               .Assert(_TimerOn == false
               , new TErrorInfo(GetType(), "Start")
               , "_TimerOn", _TimerOn, !_TimerOn);
            _StartPoint = DateTime.Now;
            _TimerOn = true;
        }
        public void MakeProgress()
        {
            SErrorManager.Errors("IndexOutOfRange")
                .Assert(_Index < _Denominator
                , new TErrorInfo(GetType(), "MakeProgress")
                , _Index
                , _Denominator);

            _TimerRunningEventArgs.RemainedTime
                 = RemainedTime(_Index);
            _TimerRunningEventArgs.CompletionRatio
                = CompletionRatio(_Index);
            _Events.OnTimerRunning(_TimerRunningEventArgs);
            _Index++;
        }
        public void MakeProgress(int i)
        {
            SErrorManager.Errors("IndexOutOfRange")
                .Assert(_Index < _Denominator
                , new TErrorInfo(GetType(), "MakeProgress")
                , _Index
                , _Denominator);
            
            _Index = i;
            _TimerRunningEventArgs.RemainedTime
                 = RemainedTime(_Index);
            _TimerRunningEventArgs.CompletionRatio
                = CompletionRatio(_Index);
            _Events.OnTimerRunning(_TimerRunningEventArgs);
        }
        public bool IsActive()
        {
            bool r = _Index < Convert.ToInt32(_Denominator);
            if (!r)
                Stop();
            return r;
        }
        public TimeSpan Stop()
        {
            _Index = 0;
            SErrorManager.Errors("BooleanFlagWrong")
               .Assert(_TimerOn == true
               , new TErrorInfo(GetType(), "Stop")
               , "_TimerOn", _TimerOn, !_TimerOn);
            _TimerOn = false;
            TimeSpan end = DateTime.Now.Subtract(_StartPoint);
            _Events.OnTimerEnded(new TTimerEndedEventArgs(end));
            return end;
        }
        #endregion

        #region private methods
        private double CompletionRatio(int i)
        {
            return Convert.ToDouble(i) / _Denominator;
        }
        private TimeSpan RemainedTime(int i)
        {
            if (i == 0)
            {
                _LastPinnedTime = DateTime.Now;
                return new TimeSpan();
            }
            else if (i <= _UnitCount)
            {
                _UnitTimes[i - 1] = DateTime.Now.Subtract(_LastPinnedTime);
                _LastPinnedTime = DateTime.Now;
                return new TimeSpan();
            }
            else if (i % (_UnitCount + 1) == 0)
            {
                TimeSpan unitTime = GetAverageUnitTime();
                int limit = Convert.ToInt32(_Denominator);
                _EvaluatedCompletionTime = DateTime.Now;
                for (int j = i; j < limit + 1; j++)
                {
                    _EvaluatedCompletionTime =
                        _EvaluatedCompletionTime.Add(unitTime);
                }
            }
            return _EvaluatedCompletionTime.Subtract(DateTime.Now);
        }
        private TimeSpan GetAverageUnitTime()
        {
            long ticks = 0;
            for (int i = 0; i < _UnitCount; i++)
            {
                ticks += _UnitTimes[i].Ticks;
            }
            return new TimeSpan(ticks / _UnitCount);
        }
        #endregion

        #region properties
        private TTimerEvents _Events;
        public TTimerEvents Events
        {
            get { return _Events; }
        }

        #endregion
    }
}
